/**
 * 
 */
package com.gr.experimental.model.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.gr.experimental.model.Distribution;
import com.gr.experimental.model.RVClass;
import com.gr.experimental.model.RVClassInfluence;
import com.gr.experimental.model.UID;
import com.gr.experimental.model.impl.DistributionUtil.StateConfigurationDescriptor;


/**
 * A class for defining distribution for a constant number of well specified parents.
 * @author Karol Banczyk
 *
 */
class ConstDistribution implements Distribution {

	
	private List<UID> domain;
	StateConfigurationDescriptor stateConfigurationDescriptor;
//	private int[][][] complexStateConfigurations;
//	private int parentConfigSize =
	private double[] defaultDistr;
	private double distrs[][];
	List<ObjectCount<RVClass>> conditionRVClassesCounts;
	List<RVClass> conditionRVClasses;
	
	/**
	 * The constructor for parent dependent distribution.
	 * @param domain the domain of the distribution,
	 * @param parents the parents,
	 * @param distrs a list of probabilities in the conventional ordering (as in 
	 * {@link DistributionUtil#generateComplexStateConfigurations()}).  
	 */
	public ConstDistribution(List<UID> domain, List<ObjectCount<RVClass>> parents, double[][] distrs, double[] defaultDistr) {
		
		this.conditionRVClassesCounts = new ArrayList<ObjectCount<RVClass>>(parents);
		if (parents == null) {
			throw new IllegalArgumentException("Argument parents is null");
		}
		this.conditionRVClasses = new ArrayList<RVClass>();
		
		for (ObjectCount<RVClass> rvClassCount: parents) {
			conditionRVClasses.add(rvClassCount.getObject());
		}
		
		int[][] stateHolderNumberPairs = new int[parents.size()][2];
		for (int i=0; i< stateHolderNumberPairs.length; i++) {
			stateHolderNumberPairs[i][0] = parents.get(i).getCount();
			stateHolderNumberPairs[i][1] = parents.get(i).getObject().getDomain().size();
		}
		
		
	//	complexStateConfigurations = DistributionUtil.generateComplexStateConfigurations(stateHolderNumberPairs);
		stateConfigurationDescriptor = DistributionUtil.createStateConfigurationDescriptor(stateHolderNumberPairs);
		
		if (distrs.length != stateConfigurationDescriptor.countStateVariationSize() ) {
			throw new IllegalArgumentException("distrs.length != complexStateConfigurations.length .");
		}
		this.distrs = distrs;
		this.defaultDistr = defaultDistr; 
	}
	
//	public ConstDistribution(List<UID> domain, Map<List<RVClassInfluence>, double[]> distr) {
//		this.domain = domain;
//		if (distr.size() > 0 ) { // check consistency
//			List<RVClassInfluence> firstInfluence = distr.entrySet().iterator().next().getKey();
//			List<RVClass> firstRowRvClasses = getRVClassesForInfluences(firstInfluence);
//			int[] rvInfluenceCounts = getRvInfluenceCounts(firstInfluence);
//			for (Entry<List<RVClassInfluence>, double[]> e : distr.entrySet()) {
//				
//				List<RVClass> classesForInfluences = getRVClassesForInfluences(e.getKey());
//				if (!firstRowRvClasses.equals(classesForInfluences)) {
//					throw new IllegalArgumentException("rows with different rv classes " + firstRowRvClasses  +" and "+ classesForInfluences);
//				}
//				if (!Arrays.equals(rvInfluenceCounts, getRvInfluenceCounts(e.getKey()))) {
//					throw new IllegalArgumentException("rows with different class influence counts " 
//							+ Arrays.asList(rvInfluenceCounts)  + " and " 
//							+ Arrays.asList(getRvInfluenceCounts(e.getKey())) 
//							+ " for rv classes:" + firstRowRvClasses);
//				}
//			}
//		}
//		
//	}
	
	public ConstDistribution(List<UID> domain, double[] defaultDistr) {
//		this(domain, new HashMap<List<RVClassInfluence>, List<UIDProbability>>())
		this.domain = domain;
		this.distrs = new double[0][];
		this.defaultDistr = defaultDistr;
		this.conditionRVClasses = Collections.emptyList();
	}
	
//	private int[] getRvInfluenceCounts(List<RVClassInfluence> influences) {
//		int[] influenceCounts = new int[influences.size()];
//		int i = 0;
//		for (RVClassInfluence influence: influences) {
//			influenceCounts[i] = influence.countRvClassInfluenceCount();
//			i ++;
//		}
//		return influenceCounts;
//	}
//	
//	private List<RVClass> getRVClassesForInfluences(List<RVClassInfluence> rvClassInfluences) {
//		List<RVClass> rvClasses = new ArrayList<RVClass>(rvClassInfluences.size());
//		for (RVClassInfluence rvClassInfluence: rvClassInfluences) {
//			rvClasses.add(rvClassInfluence.getRVClass());
//		}
//		return rvClasses;
//	}

	/**
	 * {@inheritDoc}
	 * @see com.gr.exp.reasoner.Distribution#getProbabilities(java.util.List)
	 */
	@Override
	public double[] getProbabilities(List<RVClassInfluence> partialWorldState) {
		
		if (partialWorldState == null || partialWorldState.size() == 0) {
			return getProbabilities();
		}
		

		int [][] configuration = new int[conditionRVClassesCounts.size()][];
		
		for (int i=0; i< configuration.length; i++) {
			configuration[i] = new int[conditionRVClassesCounts.get(i).getObject().getDomain().size()];
//			System.out.println("state[" + i +"]=" + state[i].length);
		}
		
		
		// first we assume proper order
		int i=0;
		for (RVClassInfluence influence: partialWorldState) {
			RVClass rvClass = influence.getRVClass();
			int[] stateInfluences = influence.getRVStateInfluenceCounts();
			RVClass parent = conditionRVClassesCounts.get(i).getObject();
			if (! parent.equals(rvClass)) {
//				System.out.println("parents.get(" + i + ")=" + parents.get(i).get);
//				System.out.println("rvClass=" + rvClass);
				throw new IllegalArgumentException("influences not in parents order");
			}
			if (conditionRVClassesCounts.get(i).getCount() != influence.getRvClassInfluenceCount()) {
				throw new IllegalArgumentException("influences for parent: " + parent.getName() + " of bad size," +
						" is: " + influence.getRvClassInfluenceCount() + " and should be: " + 
						conditionRVClassesCounts.get(i).getCount());
			}
//			for (ObjectCount<RVState> rvStateCount : inf.getRvStates()) {
//				RVState rvState = rvStateCount.getObject();
//				// get x for state[x][]
//				int x = -1,y=-1;
//				
//				loop:
//				for (int k=0; k< parents.size(); k++) {
//					RVClass aRvClass = parents.get(k).getObject();
//					if (aRvClass.equals(rvClass)) { // found parent
//						x = k;
//						y = aRvClass.getDomain().indexOf(rvState);
//						break loop;
//					}
//				}
//				if (y == -1 || x == -1) {
//					throw new IllegalArgumentException("RVState not found" );
//				}
			
//				System.out.println("x=" + x + ", y=" + y);
			for (int j=0; j < stateInfluences.length; j++) {
				configuration[i][j] += stateInfluences[j];
			}
			i++;
		}

		// finish creating index for partialWorld state
		
		int indexOfConfig = stateConfigurationDescriptor.indexOfConfig(configuration);
		
		if (indexOfConfig < 0 ) {
			// consider special exception for the case
			throw new IllegalArgumentException("parent configuration not found");
		}
		return distrs[indexOfConfig];
		
		//for non empty parent config the method not yet implemented
	}
	

	@Override
	public double[] getProbabilities() {
		
		// this is the default distribution
		return defaultDistr;
	}

	/**
	 * {@inheritDoc}
	 * @see com.gr.exp.reasoner.Distribution#getDomain()
	 */
	@Override
	public List<UID> getDomain() {
		return domain;
	}

	/**
	 * {@inheritDoc}
	 * @see com.gr.experimental.model.Distribution#getConditionRVClasses()
	 */
	@Override
	public List<RVClass> getConditionRVClasses() {
		return this.conditionRVClasses;
	}
	
//	/**
//	 * {@inheritDoc}
//	 * @see com.gr.exp.reasoner.DistrSpec#getDistribution(java.util.List)
//	 */
//	@Override
//	public double[] getDistribution(List<ObjectCount<RVClass>> partialWorldState) {
//		 {
//				
//				// must return probabilities 
//				// of respective domain elements 
//				double distr[] = new double[domain.size()];
//				for (int i=0; i < distr.length; i++) {
//					distr[i] = 1.0 / distr.length;
//				}
//				return distr;
//			}
//	}

}
